// $Id: uuid.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#define XPCOM_SOURCE

#include <xpcom/config.hpp>
#include <cstring> // for memcmp
#include <algorithm>
#include <xpcom/char_traits.hpp>
#include <xpcom/exception.hpp>
#include <xpcom/result_code.hpp>
#include <xpcom/string.hpp>
#include <xpcom/uuid.hpp>
#include <xpcom/detail/hex_format.hpp>

namespace xpcom {

namespace {

// Length of string for conversion
const uint32_t string_len = 36;
const uint32_t string_len_with_braces = 38;

// TODO Illegal character (which is not hex digits) is not checked.
template <typename CharT>
const CharT* read_str(uuid& id, const CharT* s, uint32_t size) throw()
{
    if (!s || size < string_len)
        return 0;

    bool has_braces = false;
    if (*s == CharT('{')) {
        if (size < string_len_with_braces)
            return 0;
        has_braces = true;
        ++s;
    }

    s = detail::hex_read(s, id.m0);
    if (*s != CharT('-'))
        return 0;
    ++s;

    s = detail::hex_read(s, id.m1);
    if (*s != CharT('-'))
        return 0;
    ++s;

    s = detail::hex_read(s, id.m2);
    if (*s != CharT('-'))
        return 0;
    ++s;

    s = detail::hex_read(s, id.m3[0]);
    s = detail::hex_read(s, id.m3[1]);
    if (*s != CharT('-'))
        return 0;
    ++s;

    for (int i = 2; i < 8; ++i) {
        s = detail::hex_read(s, id.m3[i]);
    }

    if (has_braces) {
        if (*s != CharT('}'))
            return 0;
        ++s;
    }

    return s;
}

template <typename CharT>
CharT* write_str(const uuid& id, CharT* s, uint32_t size, uint32_t flags) throw()
{
    if (!s || size < string_len)
        return 0;

    bool has_braces = false;
    if (flags & uuid::output_braces) {
        if (size < string_len_with_braces)
            return 0;
        has_braces = true;
        *s++ = CharT('{');
    }

    const char* hex_tab = 0;
    bool output_default = false;
    if (flags & uuid::output_lower_case) // lower case has priority
        hex_tab = detail::hex_tab_lower();
    else if (flags & uuid::output_upper_case)
        hex_tab = detail::hex_tab_upper();
    else {
        hex_tab = detail::hex_tab_upper();
        output_default = true;
    }

    s = detail::hex_write(s, id.m0, hex_tab);
    *s++ = CharT('-');

    s = detail::hex_write(s, id.m1, hex_tab);
    *s++ = CharT('-');

    if (output_default)
        hex_tab = detail::hex_tab_lower();
    s = detail::hex_write(s, id.m2, hex_tab);
    *s++ = CharT('-');
    if (output_default)
        hex_tab = detail::hex_tab_upper();

    s = detail::hex_write(s, id.m3[0], hex_tab);
    s = detail::hex_write(s, id.m3[1], hex_tab);
    *s++ = CharT('-');

    for (int i = 2; i < 8; ++i) {
        s = detail::hex_write(s, id.m3[i], hex_tab);
    }

    if (has_braces) {
        *s++ = CharT('}');
    }

    return s;
}

} // anonymous namespace

const uuid uuid::nil = {};

int uuid::compare(const uuid& rhs) const throw()
{
    int i = m0 == rhs.m0 ? 0 : (m0 < rhs.m0 ? -1 : 1);
    if (i == 0 &&
        (i = ((int32_t)m1 - (int32_t)rhs.m1)) == 0 &&
        (i = ((int32_t)m2 - (int32_t)rhs.m2)) == 0)
    {
        i = std::memcmp(m3, rhs.m3, 8);
    }
    return i;
}

void uuid::swap(uuid& rhs) throw()
{
    std::swap(*this, rhs);
//    std::swap(((uint32_t*)&m0)[0], ((uint32_t*)&rhs.m0)[0]);
//    std::swap(((uint32_t*)&m0)[1], ((uint32_t*)&rhs.m0)[1]);
//    std::swap(((uint32_t*)&m0)[2], ((uint32_t*)&rhs.m0)[2]);
//    std::swap(((uint32_t*)&m0)[3], ((uint32_t*)&rhs.m0)[3]);
}

template <typename CharT>
void uuid::from_str(const basic_string<CharT>& str)
{
    if (!read_str(*this, str.data(), str.size()))
        throw_invalid_argument();
}

template <typename CharT>
void uuid::from_str(const CharT* s, uint32_t n)
{
    if (n == static_cast<uint32_t>(-1))
        n = static_cast<uint32_t>(char_traits<CharT>::length(s));

    if (!read_str(*this, s, n))
        throw_invalid_argument();
}

template <typename CharT>
void uuid::to_str(basic_string<CharT>& str, uint32_t flags) const
{
    typedef basic_string<CharT> string_type;

    const uint32_t len = (flags & output_braces) ?
            string_len_with_braces : string_len;

    str.resize(len);
    write_str(*this, &str[0], str.size(), flags);
}

template XPCOM_DECL void uuid::from_str(const basic_string<char>&);
template XPCOM_DECL void uuid::from_str(const basic_string<wchar>&);
template XPCOM_DECL void uuid::from_str(const char*, uint32_t);
template XPCOM_DECL void uuid::from_str(const wchar*, uint32_t);
template XPCOM_DECL void uuid::to_str(basic_string<char>&, uint32_t) const;
template XPCOM_DECL void uuid::to_str(basic_string<wchar>&, uint32_t) const;

string uuid::str(uint32_t flags) const
{
    string s;
    to_str(s, flags);
    return s;
}

wstring uuid::wstr(uint32_t flags) const
{
    wstring s;
    to_str(s, flags);
    return s;
}

} // namespace xpcom
